#include <filesystem>
#include <fstream>
#include "mmap_output_strategy.h"
#include "gtest/gtest.h"

namespace Base {
namespace SystemLog {
namespace {
constexpr const char* TEST_LOG_DIR = "out/tests/logs";
constexpr const char TEST_CHAR_ONE = 't';
constexpr const char TEST_CHAR_TWO = 'u';
constexpr size_t MB = 1024 * 1024; // 1MB
constexpr size_t TEST_MAPPING_SIZE = 10 * MB; // 10MB
constexpr size_t TEST_MMAP_OUTPUT_DATA_SIZE = 5 * MB; // 5MB
constexpr size_t TEST_SMALL_MAPPING_SIZE = 1024; // 1KB
constexpr size_t TEST_SMALL_MMAP_OUTPUT_DATA_SIZE_ONE = 512; // 0.5KB
constexpr size_t TEST_SMALL_MMAP_OUTPUT_DATA_SIZE_TWO = 716;
} // namespace

class MMapOutputStrategyTest : public testing::Test {
public:
    static void SetUpTestCase(void);
    static void TearDownTestCase(void);
    void SetUp();
    void TearDown();
};

void MMapOutputStrategyTest::SetUpTestCase(void)
{}

void MMapOutputStrategyTest::TearDownTestCase(void)
{}

void MMapOutputStrategyTest::SetUp(void)
{
    std::filesystem::create_directories(TEST_LOG_DIR);
}

void MMapOutputStrategyTest::TearDown(void)
{
    try {
        if (std::filesystem::exists(TEST_LOG_DIR)) {
            std::filesystem::remove_all(TEST_LOG_DIR);
        } else {
            GTEST_LOG_(ERROR) << "directory does not exist: " << TEST_LOG_DIR << '\n';
        }
    } catch (const std::filesystem::filesystem_error &ex) {
        GTEST_LOG_(ERROR) << "deletion failed: " << ex.what() << '\n';
    }
}

/**
 * @tc.name: MMapOutputStrategyTest_001
 * @tc.desc: verify Write function.
 * @tc.type: FUNC
 */
TEST_F(MMapOutputStrategyTest, MMapOutputStrategyTest_001)
{
    MMapOutputOptions options;
    options.filePathPattern = std::string(TEST_LOG_DIR) + "/mmap_output_strategy_test.log";
    options.mappingSize = TEST_MAPPING_SIZE;

    MMapOutputStrategy mmapStrategy(options);
    std::vector<char> largeData(TEST_MMAP_OUTPUT_DATA_SIZE, TEST_CHAR_ONE);
    ASSERT_EQ(mmapStrategy.Write(largeData.data(), TEST_MMAP_OUTPUT_DATA_SIZE), ERR_OK);
    ASSERT_EQ(mmapStrategy.Flush(), ERR_OK);
    ASSERT_EQ(mmapStrategy.Close(), ERR_OK);

    // 验证文件大小
    auto fileSize = std::filesystem::file_size(options.filePathPattern);
    ASSERT_EQ(fileSize, TEST_MMAP_OUTPUT_DATA_SIZE);
}

/**
 * @tc.name: MMapOutputStrategyTest_002
 * @tc.desc: verify mmap remapping function.
 * @tc.type: FUNC
 */
TEST_F(MMapOutputStrategyTest, MMapOutputStrategyTest_002)
{
    MMapOutputOptions options;
    options.filePathPattern = std::string(TEST_LOG_DIR) + "/remap.log";
    options.mappingSize = TEST_SMALL_MAPPING_SIZE;
    
    MMapOutputStrategy strategy(options);
    
    // 第一次写入 (0.5KB)
    std::string data1(TEST_SMALL_MMAP_OUTPUT_DATA_SIZE_ONE, TEST_CHAR_ONE);
    ASSERT_EQ(strategy.Write(data1.data(), data1.size()), ERR_OK);
    
    // 第二次写入 (0.7KB) 会触发重映射
    std::string data2(TEST_SMALL_MMAP_OUTPUT_DATA_SIZE_TWO, TEST_CHAR_TWO);
    ASSERT_EQ(strategy.Write(data2.data(), data2.size()), ERR_OK);
    
    ASSERT_EQ(strategy.Close(), ERR_OK);
    
    // 验证总大小
    auto fileSize = std::filesystem::file_size(options.filePathPattern);
    ASSERT_EQ(fileSize, TEST_SMALL_MMAP_OUTPUT_DATA_SIZE_ONE + TEST_SMALL_MMAP_OUTPUT_DATA_SIZE_TWO);
}
} // namespace SystemLog
} // namespace Base
